package control;

import java.util.Calendar;
import java.util.Vector;

import storage.ManageData;
import utils.CommandAnalyzer;
import basic.CRUDable;
import basic.Date;
import basic.Manager;
import domain.Category;
import domain.Expense;
import domain.FamilyMember;
import domain.PaymentMethod;
import enums.Frequency;


//@author A0098891H
public class ExpenseManager extends Manager {

	private Vector<Expense> _listOfExpenses;
	private MemberManager _memMan;
	private PaymentMethodManager _payMan;
//	private CategoryManager _catMan;
	

	/**
	 * @param budget
	 * @param memMan
	 * @param payMan
	 */
	public ExpenseManager(MemberManager memMan, PaymentMethodManager payMan)
	{
		this._memMan = memMan;
		this._payMan = payMan;
//		this._catMan = catMan;
		this.initialize();
	}
	
	
	/**
	 * 
	 * @return
	 * @Precondition 
	 * @Postcondition 
	 */
	public Vector<Expense> getList()
	{
		return this._listOfExpenses;
	}
	
	/**
	 *
	 * @see basic.Manager#add(basic.CRUDable)
	 * @param item
	 * @return
	 * @Precondition 
	 * @Postcondition 
	 */
	@Override
	public int add(CRUDable item) {
		item.set_id(_nextId++);
		if(this._listOfExpenses.add((Expense)item))
		{
			((Expense) item).set_lastUpdateDate(((Expense)item).get_date());
			checkRecurExpenses((Expense)item);
			this._totalAmountOfAll+=((Expense)item).get_totalAmount();
			return item.get_id();
		}
		else
		{
			_nextId--;
			return -1;
		}
	}
	
	/**
	 *
	 * @see basic.Manager#addTo(basic.CRUDable, int, int)
	 * @param item
	 * @param id
	 * @param pos
	 * @return
	 * @Precondition 
	 * @Postcondition 
	 */
	@Override
	public boolean addTo(CRUDable item, int id,int pos) {
		item.set_id(id);
		((Expense)item).set_lastUpdateDate(((Expense) item).get_lastUpdateDate());
		checkRecurExpenses((Expense) item);
		this._listOfExpenses.add(pos,(Expense)item);
		this._totalAmountOfAll+=((Expense)item).get_totalAmount();
		return true;
	}
	

	/**
	 *
	 * @see basic.Manager#get(int)
	 * @param id
	 * @return
	 * @Precondition 
	 * @Postcondition 
	 */
	@Override
	public Expense get(int id) {
		for(Expense xpn: _listOfExpenses)
		{
			if(xpn.get_id()==id)
				return xpn;
		}
		return null;
	}

	/**
	 *
	 * @see basic.Manager#edit(int, basic.CRUDable)
	 * @param id
	 * @param item
	 * @return
	 * @Precondition 
	 * @Postcondition 
	 */
	@Override
	public Expense edit (int id, CRUDable item) {
		for(Expense xpn: _listOfExpenses)
		{
			if(xpn.get_id()==id)
			{
				Expense expense = new Expense(xpn);
				xpn.set((Expense)item);//this casting is necessary
				if(((Expense)item).get_frequency()==Frequency.STOPPED)
				{
					xpn.set_lastUpdateDate(expense.get_lastUpdateDate());
				}
				else
				{
					xpn.set_lastUpdateDate(((Expense)item).get_date());
				}
				xpn.set_totalAmount(expense.get_totalAmount());
				checkRecurExpenses(xpn);
				this._totalAmountOfAll-= expense.get_totalAmount();
				this._totalAmountOfAll+=xpn.get_totalAmount();

				return expense;
			}
		}
		return null;
	}

	/**
	 *
	 * @see basic.Manager#delete(int)
	 * @param id
	 * @return
	 * @Precondition 
	 * @Postcondition 
	 */
	@Override
	public Expense delete(int id) {
		//need to modify
		for(Expense xpn: _listOfExpenses)
		{
			if(xpn.get_id()==id)
			{
				if(_listOfExpenses.remove(xpn))
				{
					this._totalAmountOfAll-= xpn.get_totalAmount();
					return xpn;
				}
				else
					return null;
			}
		}
		return null;
	}

	public int deleteFrom(int id) {
		//need to modify
		int i=0;
		for(Expense xpn: _listOfExpenses)
		{
			if(xpn.get_id()==id)
			{
				if(_listOfExpenses.remove(xpn))
				{
					this._totalAmountOfAll-= xpn.get_totalAmount();
					return i;
				}
				else
					return -1;
			}
			i++;
		}
		return -1;
	}

	/**
	 *
	 * @see interfaces.CommandLine#readCmd(java.lang.String)
	 * @param cmd
	 * @return
	 * @Precondition 
	 * @Postcondition 
	 */
	@Override
	public int readCmd(String cmd) {
		//<EXPENSE>|<ADD> <NAME> <AMOUNT> (COMPULSORY)
		//<EXPENSE>|<EDIT> <ID> <NAME> <AMOUNT> (COMPULSORY) 
		//OPTIONAL BUT NEED PARAMETER c:<CATEGORY_ID> d:<DATE> m:<MEMBER_ID> pm:<PAYMENTMODE> pt:<PAIDTO> f:<FREQUENCY_TYPE> cm:<COMMENTS>
		
		//<EXPENSE>|<DELETE> <ID>
		//<EXPENSE>|<VIEW> <ID>
		//<EXPENSE>|<UNDO>

		//check undo
		boolean isUndo = false;
		boolean isRedo = false;
		if(cmd.trim().compareToIgnoreCase("undo")==0)
		{
			if(!_undoStack.isEmpty())
			{
				isUndo = true;
				cmd = _undoStack.pop();
				int iAction = cmd.indexOf('[');
				int isavedId = cmd.indexOf('&');
				int isavedPos = cmd.indexOf(']');
				_undoAction = Integer.parseInt(cmd.substring(0, iAction));
				_savedId = Integer.parseInt(cmd.substring(iAction+1, isavedId));
				_savedPos = Integer.parseInt(cmd.substring(isavedId+1, isavedPos));

				if(_undoAction==ACTION_DELETE || _undoAction==ACTION_EDIT)
				{
					_redoStack.add(cmd.substring(0,cmd.indexOf('|')));
					cmd = cmd.substring(cmd.indexOf('|')+1);
				}
			}
		}
		else if(cmd.trim().compareToIgnoreCase("redo")==0)
		{
			if(!_redoStack.isEmpty())
			{
				isRedo = true;
				cmd = _redoStack.pop();
				if(cmd.indexOf(']')!=-1)
				{
					int iAction = cmd.indexOf('[');
					int isavedId = cmd.indexOf('&');
					int isavedPos = cmd.indexOf(']');
					_undoAction = Integer.parseInt(cmd.substring(0, iAction));
					_savedId = Integer.parseInt(cmd.substring(iAction+1, isavedId));
					_savedPos = Integer.parseInt(cmd.substring(isavedId+1, isavedPos));
					cmd = cmd.substring(cmd.indexOf(']')+1);
				}
			}	
		}
		
		String str[] = cmd.split(",");
		for(int i=0;i<str.length;i++)
		{
			str[i] = str[i].trim();
		}
		int length = str.length;
		if(length < 2)
			return -1;
		switch(str[1].toLowerCase())
		{
		case "add":
			if(length < 4) 
				return -1;
			Expense xpn = new Expense();
			if(str[2].trim().isEmpty())
			{
				return -1;
			}
			xpn.set_name(str[2]);
			try
			{
				xpn.set_amount(Double.parseDouble(str[3]));
			}
			catch(NumberFormatException e)
			{
				return -1;
			}
			xpn.set_category(CommandAnalyzer.getCategoryID(str));
			if(!CommandAnalyzer.validateDateForExpenseNIncome(str))
				return -1;
			xpn.set_date(CommandAnalyzer.getDate(str));
			xpn.set_byMem(_memMan.get(CommandAnalyzer.getMemberID(str)));
			xpn.set_paymentMethod(_payMan.get(CommandAnalyzer.getPaymentMethodID(str)));
			xpn.set_partyInvolved(CommandAnalyzer.getPartyInvolved(str));
			xpn.set_frequency(CommandAnalyzer.getFrequency(str));
			xpn.set_comments(CommandAnalyzer.getComments(str));
			xpn.set_lastUpdateDate(CommandAnalyzer.getUpdateDate(str));
			xpn.set_totalAmount(CommandAnalyzer.getTotalAmount(str));
			_returnedObject = xpn;
			if(isUndo && _undoAction==ACTION_ADD)
			{
				int _savedPos = deleteFrom(_savedId);
				if(_savedPos >= 0)
				{//must record the position and id for redo
					_redoStack.add(-1+"["+_savedId+"&"+_savedPos+"]"+cmd);
					return _undoAction;
				}
				else
					return -1;//undo failed
			}
			if(isUndo && _undoAction==ACTION_DELETE)
			{
				if(!addTo(xpn,_savedId,_savedPos))
					return -1;
//				System.out.println("In 'isUndo if statement");
				return _undoAction;
			}

			if(isRedo)
			{
				if(!addTo(xpn,_savedId,_savedPos))
					return -1;
			}
			else
			{
				_savedId = add(xpn);
			}
			if(_savedId<0)
				return -1;
			_action = ACTION_ADD;
			break;
		case "delete":
			if(length < 3)
				return -1;
			try
			{
				_savedId = Integer.parseInt(str[2]);
				Expense deletedXpns = get(_savedId);
				if(deletedXpns==null) 
					return -1;
				_savedPos = this._listOfExpenses.indexOf(deletedXpns);
				deletedXpns = delete(_savedId);
				if(deletedXpns==null)
					return -1;
				_returnedObject = deletedXpns;
				cmd+="|expense,add,"+deletedXpns.toString();
				_action = ACTION_DELETE;
			}
			catch(NumberFormatException e)
			{
				return -1;
			}
			
			break;
		case "edit":
			if(length < 5)
				return -1;
			try
			{
				_savedId = Integer.parseInt(str[2]);
//				System.out.println("_savedId is " + _savedId);
				Expense updatedXpn = new Expense();
				if(str[3].trim().isEmpty())
				{
					return -1;
				}
				updatedXpn.set_name(str[3]);
				updatedXpn.set_amount(Double.parseDouble(str[4]));
				updatedXpn.set_category(CommandAnalyzer.getCategoryID(str));
				if(!CommandAnalyzer.validateDateForExpenseNIncome(str))
					return -1;
				updatedXpn.set_date(CommandAnalyzer.getDate(str));
				updatedXpn.set_byMem(_memMan.get(CommandAnalyzer.getMemberID(str)));
				updatedXpn.set_paymentMethod(_payMan.get(CommandAnalyzer.getPaymentMethodID(str)));
				updatedXpn.set_partyInvolved(CommandAnalyzer.getPartyInvolved(str));
				updatedXpn.set_frequency(CommandAnalyzer.getFrequency(str));
				updatedXpn.set_comments(CommandAnalyzer.getComments(str));
				updatedXpn.set_lastUpdateDate(CommandAnalyzer.getUpdateDate(str));
				updatedXpn.set_totalAmount(CommandAnalyzer.getTotalAmount(str));

				Expense obsoleteExpense = edit(_savedId,updatedXpn);
				if(obsoleteExpense==null)
					return -1;

				_returnedObject = obsoleteExpense;
				if(isUndo && _undoAction==ACTION_EDIT)
				{
						get(_savedId).set_totalAmount(updatedXpn.get_totalAmount());
						get(_savedId).set_lastUpdateDate(updatedXpn.get_lastUpdateDate());
					return _undoAction;
				}
				
				cmd+="|expense,edit,"+_savedId+","+obsoleteExpense.toString();
				_action = ACTION_EDIT;
			}
			catch(NumberFormatException e)
			{
				return -1;
			}
			
			break;
		default:
			return -1;
		}

		_undoStack.add(_action+"["+ _savedId +"&"+ _savedPos +"]"+cmd);
		return _action;
	}
	
	/**
	 *
	 * @see java.lang.Object#toString()
	 * @return
	 * @Precondition 
	 * @Postcondition 
	 */
	public String toString()
	{
		StringBuilder sb = new StringBuilder();
		for(Expense xpn : this._listOfExpenses)
		{
			sb.append("  ("+xpn.get_id()+") "+xpn.toString() + "\n");
		}
		sb.append("total expense: "+this._totalAmountOfAll +"\n");
		return sb.toString();
	}
	
	/**
	 * 
	 * @param xpnManager
	 * @Precondition 
	 * @Postcondition 
	 */
	public void set(ExpenseManager xpnManager)
	{
		super.set(xpnManager);
		this._listOfExpenses = xpnManager._listOfExpenses;
	}
	
	/**
	 *
	 * @see basic.Manager#saveAll()
	 * @return
	 * @Precondition 
	 * @Postcondition 
	 */
	@Override
	public boolean saveAll() {
		return ManageData.saveExpenseManagerToFile(this);
	}

	/**
	 *
	 * @see basic.Manager#loadAll()
	 * @return
	 * @Precondition 
	 * @Postcondition 
	 */
	@Override
	public boolean loadAll() {
		ExpenseManager xpnManager = ManageData.loadExpenseManagerFromFile();
		if(xpnManager==null)
			return false;
		
		this.set(xpnManager);
		return true;
	}
	
	/**
	 *
	 * @see basic.Manager#initialize()
	 * @return
	 * @Precondition 
	 * @Postcondition 
	 */
	@Override
	public boolean initialize() {
		// TODO Auto-generated method stub
		if(loadAll()==false)
		{
			super.initialize();
			this._listOfExpenses = new Vector<Expense>();
		}
		checkAllRecurExpenses();
		
		return true;
	}

	//return false if nothing
	/**
	 * 
	 * @return
	 * @Precondition 
	 * @Postcondition 
	 */
	public boolean checkRecurExpenses(Expense xpn)
	{	//TODO
			switch(xpn.get_frequency()) {
			case STOPPED:
				break;
			case NO:
				xpn.set_totalAmount(xpn.get_amount());
				break;
			case DAILY:
				doDailyCheck(xpn);
				break;
			case WEEKLY:
				doWeeklyCheck(xpn);
				break;	
			case MONTHLY:
				doMonthlyCheck(xpn);
				break;	
			case YEARLY:
				doYearlyCheck(xpn);
				break;
			default:
				return false;
			}
		return true;
	}
	
	public boolean checkAllRecurExpenses()
	{	//TODO
		for (Expense xpn: this._listOfExpenses) {
			 checkRecurExpenses(xpn);
		}
		
		return true;
	}
	
	private void doDailyCheck(Expense xpn) {
		// TODO Auto-generated method stub
		Date curDate = Date.getCurrentDate();
		xpn.set_totalAmount(xpn.get_amount());
		xpn.set_lastUpdateDate(xpn.get_date());
		while(curDate.isGreaterOrEqual(xpn.get_lastUpdateDate().getRelatedDate(0, 0, 0, 1)))
		{
			xpn.set_lastUpdateDate(xpn.get_lastUpdateDate().getRelatedDate(0, 0, 0, 1));
			xpn.addTotalAmount(xpn.get_amount());
		}
	}
	
	private void doWeeklyCheck(Expense xpn) {
		// TODO Auto-generated method stub
		Date curDate = Date.getCurrentDate();
		xpn.set_totalAmount(xpn.get_amount());
		xpn.set_lastUpdateDate(xpn.get_date());
		while(curDate.isGreaterOrEqual(xpn.get_lastUpdateDate().getRelatedDate(0, 0, 1, 0)))
		{
			xpn.set_lastUpdateDate(xpn.get_lastUpdateDate().getRelatedDate(0, 0, 1, 0));
			xpn.addTotalAmount(xpn.get_amount());
		}
	}
	
	private void doMonthlyCheck(Expense xpn) {
		// TODO Auto-generated method stub	
		Date curDate = Date.getCurrentDate();
		xpn.set_totalAmount(xpn.get_amount());
		xpn.set_lastUpdateDate(xpn.get_date());
		while(curDate.isGreaterOrEqual(xpn.get_lastUpdateDate().getRelatedDate(0, 1, 0, 0)))
		{
			xpn.set_lastUpdateDate(xpn.get_lastUpdateDate().getRelatedDate(0, 1, 0, 0));
			xpn.addTotalAmount(xpn.get_amount());
		}
	}
	
	private void doYearlyCheck(Expense xpn) {
		// TODO Auto-generated method stub
		Date curDate = Date.getCurrentDate();
		xpn.set_totalAmount(xpn.get_amount());
		xpn.set_lastUpdateDate(xpn.get_date());
		while(curDate.isGreaterOrEqual(xpn.get_lastUpdateDate().getRelatedDate(1, 0, 0, 0)))
		{
			xpn.set_lastUpdateDate(xpn.get_lastUpdateDate().getRelatedDate(1, 0, 0, 0));
			xpn.addTotalAmount(xpn.get_amount());
		}
	}

	/**
	 * 
	 * @param fm
	 * @return double of amount spent by mem
	 * @Precondition _listOfExpenses is initialized
	 * @Postcondition 
	 */
	//@author A0097788Y
	public double findAmountMem(FamilyMember fm){
		double amount=0;
		for(Expense xpn : this._listOfExpenses)
		{
			if (xpn.get_byMem().isSame(fm))
				amount=amount+xpn.get_totalAmount();
		}
		return amount;
	}
	
	/**
	 * 
	 * @param cat
	 * @return double of amount spent by cat
	 * @Precondition _listOfExpenses is initialized
	 * @Postcondition 
	 */
	//@author A0097788Y
	public double findAmountCat(Category cat){
		double amount=0;
		for(Expense xpn : this._listOfExpenses)
		{
			if (xpn.get_category()==cat.get_id())
				amount=amount+xpn.get_totalAmount();
		}
		return amount;
	}
	
	/**
	 * 
	 * @param pay
	 * @return double of amount spent by pay
	 * @Precondition _listOfExpenses is initialized
	 * @Postcondition 
	 */
	//@author A0097788Y
	public double findAmountPay(PaymentMethod pay){
		double amount=0;
		for(Expense xpn : this._listOfExpenses)
		{
			if (xpn.get_paymentMethod().equals(pay))
				amount=amount+xpn.get_totalAmount();
		}
		return amount;
	}
	
	/**
	 * 
	 * @param cat
	 * @param fm
	 * @return double of amount spent by cat and fm
	 * @Precondition _listOfExpenses is initialized
	 * @Postcondition 
	 */
	//@author A0097788Y
	public double findAmountCatMem(Category cat, FamilyMember fm){
		double amount=0;
		for(Expense xpn : this._listOfExpenses)
		{
			if (xpn.get_category()==cat.get_id() && xpn.get_byMem().isSame(fm))
				amount=amount+xpn.get_totalAmount();
		}
		return amount;
	}
	
	/**
	 * 
	 * @param cat
	 * @param pay
	 * @return double of amount spent by cat and pay
	 * @Precondition _listOfExpenses is initialized
	 * @Postcondition 
	 */
	//@author A0097788Y
	public double findAmountCatPay(Category cat, PaymentMethod pay){
		double amount=0;
		for(Expense xpn : this._listOfExpenses)
		{
			if (xpn.get_category()==cat.get_id() && xpn.get_paymentMethod().equals(pay))
				amount=amount+xpn.get_totalAmount();
		}
		return amount;
	}
	
	/**
	 * 
	 * @param fm
	 * @param pay
	 * @return double of amount spent by fm and pay
	 * @Precondition _listOfExpenses is initialized
	 * @Postcondition 
	 */
	//@author A0097788Y
	public double findAmountMemPay(FamilyMember fm, PaymentMethod pay){
		double amount=0;
		for(Expense xpn : this._listOfExpenses)
		{
			if (xpn.get_byMem().isSame(fm) && xpn.get_paymentMethod().equals(pay))
				amount=amount+xpn.get_totalAmount();
		}
		return amount;
	}
	
	/**
	 * 
	 * @param cat
	 * @param input
	 * @return double of amount spent by cat on Date input
	 * @Precondition _listOfExpenses is initialized
	 * @Postcondition 
	 */
	//@author A0097788Y
	public double findAmountCatDate(Category cat, Date input){
		double amount=0;
		for(Expense xpn : this._listOfExpenses)
		{
			if (xpn.get_date().equals(input) && xpn.get_category()==cat.get_id())
				amount=amount+xpn.get_amount();
		}
		return amount;
	}
	
	/**
	 * 
	 * @param fm
	 * @param input
	 * @return double of amount spent by fm on Date input
	 * @Precondition _listOfExpenses is initialized
	 * @Postcondition 
	 */
	//@author A0097788Y
	public double findAmountMemDate(FamilyMember fm, Date input){
		double amount=0;
		for(Expense xpn : this._listOfExpenses)
		{
			if (xpn.get_date().equals(input) && xpn.get_byMem().isSame(fm))
				amount=amount+xpn.get_amount();
		}
		return amount;
	}
	
	/**
	 * 
	 * @param pay
	 * @param input
	 * @return double of amount spent by pay on Date input
	 * @Precondition _listOfExpenses is initialized
	 * @Postcondition 
	 */
	//@author A0097788Y
	public double findAmountPayDate(PaymentMethod pay, Date input){
		double amount=0;
		for(Expense xpn : this._listOfExpenses)
		{
			if (xpn.get_date().equals(input) && xpn.get_paymentMethod().equals(pay))
				amount=amount+xpn.get_amount();
		}
		return amount;
	}
	
	/**
	 * 
	 * @param cat
	 * @param from
	 * @param to
	 * @return double of amount spent by cat between Date from and to
	 * @Precondition _listOfExpenses is initialized
	 * @Postcondition if frequency != "NO", the total amount until Date to will be added
	 */
	//@author A0097788Y
	public double findAmountCatDate(Category cat, Date from, Date to){
		double amount=0;
		for(Expense xpn : this._listOfExpenses)
		{
			if (!(xpn.get_category()==cat.get_id()))
				continue;
			if (xpn.get_frequency().name().equals("NO"))
				if (xpn.get_date().isGreaterOrEqual(from) && to.isGreaterOrEqual(xpn.get_date())) {
					amount=amount+xpn.get_amount();
					continue;
				}
			Calendar xpnCal = Calendar.getInstance();
			Calendar fromCal = Calendar.getInstance();
			Calendar toCal = Calendar.getInstance();
			xpnCal.set(xpn.get_date().get_year(), xpn.get_date().get_month()-1, xpn.get_date().get_day());
			fromCal.set(from.get_year(), from.get_month()-1, from.get_day());
			toCal.set(to.get_year(), to.get_month()-1, to.get_day());
			switch(xpn.get_frequency()){
			case DAILY:
				while (xpnCal.compareTo(toCal)<0) {
					xpnCal.roll(Calendar.DAY_OF_YEAR, true);
					if (xpnCal.get(Calendar.DAY_OF_YEAR)==1)
						xpnCal.set(Calendar.YEAR, xpnCal.get(Calendar.YEAR)+1);
					if (xpnCal.compareTo(fromCal)>0 && xpnCal.compareTo(toCal)<0) {
						amount=amount+xpn.get_amount();
					}
				}
				break;
			case WEEKLY:
				while (xpnCal.compareTo(toCal)<0) {
					for (int i=0; i<7; i++) {
						xpnCal.roll(Calendar.DAY_OF_YEAR, true);
						if (xpnCal.get(Calendar.DAY_OF_YEAR)==1)
							xpnCal.set(Calendar.YEAR, xpnCal.get(Calendar.YEAR)+1);
					}
					if (xpnCal.compareTo(fromCal)>0 && xpnCal.compareTo(toCal)<0) {
						amount=amount+xpn.get_amount();
					}
				}
				break;
			case MONTHLY:
				while (xpnCal.compareTo(toCal)<0) {
					xpnCal.roll(Calendar.MONTH, true);
					if (xpnCal.get(Calendar.MONTH)==1)
						xpnCal.set(Calendar.YEAR, xpnCal.get(Calendar.YEAR)+1);
					if (xpnCal.compareTo(fromCal)>=0 && xpnCal.compareTo(toCal)<=0) {
						amount=amount+xpn.get_amount();
					}
				}
				break;
			case YEARLY:
				while (xpnCal.compareTo(toCal)<0) {
					xpnCal.roll(Calendar.YEAR, true);
					if (xpnCal.compareTo(fromCal)>0 && xpnCal.compareTo(toCal)<0) {
						amount=amount+xpn.get_amount();
					}
				}
				break;
			default:
			}
		}
		return amount;
	}
	
	/**
	 * 
	 * @param fm
	 * @param from
	 * @param to
	 * @return double of amount spent by fm between Date from and to
	 * @Precondition _listOfExpenses is initialized
	 * @Postcondition if frequency != "NO", the total amount until Date to will be added
	 */
	//@author A0097788Y
	public double findAmountMemDate(FamilyMember fm, Date from, Date to){
		double amount=0;
		for(Expense xpn : this._listOfExpenses)
		{
			if (!xpn.get_byMem().isSame(fm))
				continue;
			if (xpn.get_frequency().name().equals("NO"))
				if (xpn.get_date().isGreaterOrEqual(from) && to.isGreaterOrEqual(xpn.get_date())) {
					amount=amount+xpn.get_amount();
					continue;
				}
			Calendar xpnCal = Calendar.getInstance();
			Calendar fromCal = Calendar.getInstance();
			Calendar toCal = Calendar.getInstance();
			xpnCal.set(xpn.get_date().get_year(), xpn.get_date().get_month()-1, xpn.get_date().get_day());
			fromCal.set(from.get_year(), from.get_month()-1, from.get_day());
			toCal.set(to.get_year(), to.get_month()-1, to.get_day());
			switch(xpn.get_frequency()){
			case DAILY:
				while (xpnCal.compareTo(toCal)<0) {
					xpnCal.roll(Calendar.DAY_OF_YEAR, true);
					if (xpnCal.get(Calendar.DAY_OF_YEAR)==1)
						xpnCal.set(Calendar.YEAR, xpnCal.get(Calendar.YEAR)+1);
					if (xpnCal.compareTo(fromCal)>0 && xpnCal.compareTo(toCal)<0) {
						amount=amount+xpn.get_amount();
					}
				}
				break;
			case WEEKLY:
				while (xpnCal.compareTo(toCal)<0) {
					for (int i=0; i<7; i++) {
						xpnCal.roll(Calendar.DAY_OF_YEAR, true);
						if (xpnCal.get(Calendar.DAY_OF_YEAR)==1)
							xpnCal.set(Calendar.YEAR, xpnCal.get(Calendar.YEAR)+1);
					}
					if (xpnCal.compareTo(fromCal)>0 && xpnCal.compareTo(toCal)<0) {
						amount=amount+xpn.get_amount();
					}
				}
				break;
			case MONTHLY:
				while (xpnCal.compareTo(toCal)<0) {
					xpnCal.roll(Calendar.MONTH, true);
					if (xpnCal.get(Calendar.MONTH)==1)
						xpnCal.set(Calendar.YEAR, xpnCal.get(Calendar.YEAR)+1);
					if (xpnCal.compareTo(fromCal)>=0 && xpnCal.compareTo(toCal)<=0) {
						amount=amount+xpn.get_amount();
					}
				}
				break;
			case YEARLY:
				while (xpnCal.compareTo(toCal)<0) {
					xpnCal.roll(Calendar.YEAR, true);
					if (xpnCal.compareTo(fromCal)>0 && xpnCal.compareTo(toCal)<0) {
						amount=amount+xpn.get_amount();
					}
				}
				break;
			default:
			}
		}
		return amount;
	}
	
	/**
	 * 
	 * @param pay
	 * @param from
	 * @param to
	 * @return double of amount spent by pay between Date from and to
	 * @Precondition _listOfExpenses is initialized
	 * @Postcondition if frequency != "NO", the total amount until Date to will be added
	 */
	//@author A0097788Y
	public double findAmountPayDate(PaymentMethod pay, Date from, Date to){
		double amount=0;
		for(Expense xpn : this._listOfExpenses)
		{
			if (!xpn.get_paymentMethod().equals(pay))
				continue;
			if (xpn.get_frequency().name().equals("NO"))
				if (xpn.get_date().isGreaterOrEqual(from) && to.isGreaterOrEqual(xpn.get_date())) {
					amount=amount+xpn.get_amount();
					continue;
				}
			Calendar xpnCal = Calendar.getInstance();
			Calendar fromCal = Calendar.getInstance();
			Calendar toCal = Calendar.getInstance();
			xpnCal.set(xpn.get_date().get_year(), xpn.get_date().get_month()-1, xpn.get_date().get_day());
			fromCal.set(from.get_year(), from.get_month()-1, from.get_day());
			toCal.set(to.get_year(), to.get_month()-1, to.get_day());
			switch(xpn.get_frequency()){
			case DAILY:
				while (xpnCal.compareTo(toCal)<0) {
					xpnCal.roll(Calendar.DAY_OF_YEAR, true);
					if (xpnCal.get(Calendar.DAY_OF_YEAR)==1)
						xpnCal.set(Calendar.YEAR, xpnCal.get(Calendar.YEAR)+1);
					if (xpnCal.compareTo(fromCal)>0 && xpnCal.compareTo(toCal)<0) {
						amount=amount+xpn.get_amount();
					}
				}
				break;
			case WEEKLY:
				while (xpnCal.compareTo(toCal)<0) {
					for (int i=0; i<7; i++) {
						xpnCal.roll(Calendar.DAY_OF_YEAR, true);
						if (xpnCal.get(Calendar.DAY_OF_YEAR)==1)
							xpnCal.set(Calendar.YEAR, xpnCal.get(Calendar.YEAR)+1);
					}
					if (xpnCal.compareTo(fromCal)>0 && xpnCal.compareTo(toCal)<0) {
						amount=amount+xpn.get_amount();
					}
				}
				break;
			case MONTHLY:
				while (xpnCal.compareTo(toCal)<0) {
					xpnCal.roll(Calendar.MONTH, true);
					if (xpnCal.get(Calendar.MONTH)==1)
						xpnCal.set(Calendar.YEAR, xpnCal.get(Calendar.YEAR)+1);
					if (xpnCal.compareTo(fromCal)>=0 && xpnCal.compareTo(toCal)<=0) {
						amount=amount+xpn.get_amount();
					}
				}
				break;
			case YEARLY:
				while (xpnCal.compareTo(toCal)<0) {
					xpnCal.roll(Calendar.YEAR, true);
					if (xpnCal.compareTo(fromCal)>0 && xpnCal.compareTo(toCal)<0) {
						amount=amount+xpn.get_amount();
					}
				}
				break;
			default:
			}
		}
		return amount;
	}

	
	/**
	 * 
	 * @return
	 * @Precondition 
	 * @Postcondition 
	 */
	public Expense findMostRecentPurchase() {
		if(_listOfExpenses.size()>0)
			return this._listOfExpenses.lastElement();
		return null;
	}

	
	/**
	 * 
	 * @return
	 * @Precondition 
	 * @Postcondition 
	 */
	public Expense findMostExpensivePurchase() {
		if(_listOfExpenses.size()>0)
		{
			Expense mostExpensive =this._listOfExpenses.firstElement();
			
			for (Expense xpn: this._listOfExpenses) {
				if (xpn.get_amount() > mostExpensive.get_amount())
					mostExpensive = xpn;
			}
			
			return mostExpensive;
		}
		
		return null;
	}
	
	
	/**
	 * 
	 * @return
	 * @Precondition 
	 * @Postcondition 
	 */
	public double findAmountTotal() {
			double amount = 0;
			for (Expense xpn: this._listOfExpenses) {
				amount = amount + xpn.get_amount();
			}
			return amount;
		}
	
	/**
	 * 
	 * @return
	 * @Precondition 
	 * @Postcondition 
	 */
	public int size() {
		return this._listOfExpenses.size();
	}
	
	//@author A0103947A
	public void deleteLoans (String name, double monthlyPayment, Date startDate) {
		
		Vector<Expense> tempList = new Vector<Expense>(_listOfExpenses);
		for (int i = 0; i < tempList.size(); i++) {
			Expense xpn = tempList.get(i);
			String targetName = name + " Loan";
			if (xpn.get_frequency() == Frequency.NO && xpn.get_name().equals(targetName)
				&& xpn.get_lastUpdateDate().get_day() == startDate.get_day()) {
				delete(xpn.get_id());
			}
		}
	}
}
	
